<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->

<script>

  /**
   * Supports `listeners` object.
   *
   * Example:
   *
   *
   *     Polymer({
   *
   *       listeners: {
   *         // `click` events on the host are delegated to `clickHandler`
   *         'click': 'clickHandler'
   *       },
   *
   *       ...
   *
   *     });
   *
   *
   * @class standard feature: events
   *
   */

  Polymer.Base._addFeature({

    /**
     * Object containing entries specifying event listeners to create on each
     * instance of this element, where keys specify the event name and values
     * specify the name of the handler method to call on this prototype.
     *
     * Example:
     *
     *
     *     Polymer({
     *
     *       listeners: {
     *         // `click` events on the host are delegated to `clickHandler`
     *         'tap': 'tapHandler'
     *       },
     *
     *       ...
     *
     *     });
     */
    listeners: {},

    _listenListeners: function(listeners) {
      var node, name, key;
      for (key in listeners) {
        if (key.indexOf('.') < 0) {
          node = this;
          name = key;
        } else {
          name = key.split('.');
          node = this.$[name[0]];
          name = name[1];
        }
        this.listen(node, name, listeners[key]);
      }
    },

    /**
     * Convenience method to add an event listener on a given element,
     * late bound to a named method on this element.
     *
     * @method listen
     * @param {Element} node Element to add event listener to.
     * @param {string} eventName Name of event to listen for.
     * @param {string} methodName Name of handler method on `this` to call.
     */
    listen: function(node, eventName, methodName) {
      this._listen(node, eventName,
        this._createEventHandler(node, eventName, methodName));
    },

    _boundListenerKey: function(eventName, methodName) {
      return (eventName + ':' + methodName);
    },

    _recordEventHandler: function(host, eventName, target, methodName, handler) {
      var hbl = host.__boundListeners;
      if (!hbl) {
        hbl = host.__boundListeners = new WeakMap();
      }
      var bl = hbl.get(target);
      if (!bl) {
        bl = {};
        hbl.set(target, bl);
      }
      var key = this._boundListenerKey(eventName, methodName);
      bl[key] = handler;
    },

    _recallEventHandler: function(host, eventName, target, methodName) {
      var hbl = host.__boundListeners;
      if (!hbl) {
        return;
      }
      var bl = hbl.get(target);
      if (!bl) {
        return;
      }
      var key = this._boundListenerKey(eventName, methodName);
      return bl[key];
    },

    _createEventHandler: function(node, eventName, methodName) {
      var host = this;
      var handler = function(e) {
        if (host[methodName]) {
          host[methodName](e, e.detail);
        } else {
          host._warn(host._logf('_createEventHandler', 'listener method `' +
            methodName + '` not defined'));
        }
      };
      this._recordEventHandler(host, eventName, node, methodName, handler);
      return handler;
    },

    /**
     * Convenience method to remove an event listener from a given element,
     * late bound to a named method on this element.
     *
     * @method unlisten
     * @param {Element} node Element to remove event listener from.
     * @param {string} eventName Name of event to stop listening to.
     * @param {string} methodName Name of handler method on `this` to not call
     anymore.
     */
    unlisten: function(node, eventName, methodName) {
      var handler = this._recallEventHandler(this, eventName, node, methodName);
      if (handler) {
        this._unlisten(node, eventName, handler);
      }
    },

    _listen: function(node, eventName, handler) {
      node.addEventListener(eventName, handler);
    },

    _unlisten: function(node, eventName, handler) {
      node.removeEventListener(eventName, handler);
    }
  });

</script>
